/* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
// don't change the include order
#include "pd.h"
#include "sml_errcodes.h"

/*
 * Description: APP通过SML获取指定RAID控制器的PD的日志数据
 * History: 1.2018年1月6日
 *          新生成函数
 */
gint32 sml_get_pd_log(SML_PD_LOG_S *pd)
{
    gint32 ret_val = 0;
    guint8 ctrl_index = 0;
    guint16 device_id = 0;

    if (pd == NULL) {
        return SML_ERR_NULL_DATA;
    }

    ctrl_index = pd->i_controller_index;
    device_id = pd->i_device_id;

    ret_val = smlib_get_pd_log(ctrl_index, device_id, &pd->log);

    return ret_val;
}

/*
 * Description: APP向SML dump指定RAID控制器的PD SMART信息
 * History: 1.2016年4月16日
 *          新生成函数
 */
gint32 sml_dump_pd_smart_info(SML_PD_SMART_DUMP_S *pd)
{
    gint32 ret_val = 0;
    guint8 ctrl_index = 0;
    gchar *dump_filepath = NULL;
    gchar *dump_file_shortname = NULL;
    SML_PD_BASIC_INFO_S pd_info;

    if (pd == NULL) {
        return SML_ERR_NULL_DATA;
    }

    ctrl_index = pd->i_controller_index;
    dump_filepath = pd->i_file_path;
    dump_file_shortname = pd->i_file_short_name;

    (void)memset_s(&pd_info, sizeof(SML_PD_BASIC_INFO_S), 0, sizeof(SML_PD_BASIC_INFO_S));

    ret_val = smlib_dump_smart_info(ctrl_index, dump_filepath, dump_file_shortname);

    return ret_val;
}

/*
 * Description: 作为内存信息，收集硬盘SMART信息
 * History: 1.2018年5月19日
 *          新生成函数
 */
gint32 sml_collect_hdd_smart_data(SML_PD_SMART_COLLECT_INFO_S *pd)
{
    return smlib_get_pd_smart_info(pd->i_controller_index, pd->i_device_id, &pd->smart_info);
}

/*
 * Description: 获取全量的smart信息，并将数据组装成json数组格式的字符串
 * History: 1.2019年1年25日
 * 新生成函数      PN  : UADP141678  新增获取SMART全量信息
 */
static gint32 get_all_smart_data_to_json(ATA_SMART_ATTRIBUTE_S *attr_info, ATA_SMART_THRESHOLD_ENTRY_S *threshold_info,
    Json **output_jso)
{
#define HDD_SMART_RAW_OFFSET 8

    const gchar *attr_status = NULL;
    gint32 failed_now = 0;
    gint32 failed_ever = 0;
    const gchar *disk_type = NULL;
    const gchar *disk_update = NULL;
    Json *smart_obj = NULL;
    gchar buffer[MAX_PROPERTY_VALUE_LEN] = { 0 };
    guint64 raw_value = 0;
    guint64 temp_value = 0;
    guint8 raw_value_size = 0;
    guint32 idx = 0;

    if (NULL == attr_info || NULL == threshold_info) {
        debug_log(DLOG_ERROR, "%s:input param is null.", __FUNCTION__);
        return RET_ERR;
    }

    if (JsonObjectCreate(&smart_obj) != JSON_OK) {
        debug_log(DLOG_ERROR, "%s: JsonObjectCreate failed.\n", __FUNCTION__);
        return RET_ERR;
    }
    (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%hhu", attr_info->id);
    Json *str_obj = NULL;
    JsonStringCreate(buffer, &str_obj);
    JsonObjectItemSet(smart_obj, SMART_ID, str_obj);
    Json *str_value_obj = NULL;
    (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%hhu", attr_info->current);
    JsonStringCreate(buffer, &str_value_obj);
    JsonObjectItemSet(smart_obj, SMART_VALUE, str_value_obj);
    raw_value = 0;
    raw_value_size = (guint8)G_N_ELEMENTS(attr_info->raw);
    for (idx = 0; idx < raw_value_size; idx++) { /* 结构体中数组的大小为6 */
        temp_value = attr_info->raw[idx];
        temp_value = temp_value << (HDD_SMART_RAW_OFFSET * idx);
        raw_value |= temp_value;
    }
    gint32 ret = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%" G_GUINT64_FORMAT, raw_value);
    if (0 >= ret) {
        debug_log(DLOG_ERROR, "%s:snprintf_s for raw_value failed.", __FUNCTION__);
        (void)JsonObjectRelease(smart_obj);
        return RET_ERR;
    }
    Json *str_raw_obj = NULL;
    JsonStringCreate(buffer, &str_raw_obj);
    JsonObjectItemSet(smart_obj, SMART_RAW, str_raw_obj);

    (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", attr_info->flags);
    Json *str_flag_obj = NULL;
    JsonStringCreate(buffer, &str_flag_obj);
    JsonObjectItemSet(smart_obj, SMART_FALG, str_flag_obj);

    (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", attr_info->worst);
    Json *str_worst_obj = NULL;
    JsonStringCreate(buffer, &str_worst_obj);
    JsonObjectItemSet(smart_obj, SMART_WORST, str_worst_obj);
    if (attr_info->id != threshold_info->id) {
        (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "---");
    } else {
        (void)snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%.3d", threshold_info->threshold);
    }
    Json *str_threshold_obj = NULL;
    JsonStringCreate(buffer, &str_threshold_obj);
    JsonObjectItemSet(smart_obj, SMART_THRESHOLD, str_threshold_obj);

    disk_type = IS_ATA_SMART_ATTRIBUTE_FLAGS_VALID(attr_info->flags, 0) ? "Pre-fail" : "Old_age";
    Json *str_type_obj = NULL;
    JsonStringCreate(disk_type, &str_type_obj);
    JsonObjectItemSet(smart_obj, SMART_TYPE, str_type_obj);

    disk_update = IS_ATA_SMART_ATTRIBUTE_FLAGS_VALID(attr_info->flags, 1) ? "Always" : "Offline";
    Json *str_update_obj = NULL;
    JsonStringCreate(disk_update, &str_update_obj);
    JsonObjectItemSet(smart_obj, SMART_UPDATED, str_update_obj);

    if (threshold_info->threshold != 0) {
        failed_now = 0;
        failed_ever = 0;
    } else {
        failed_now = (attr_info->current <= threshold_info->threshold);
        failed_ever = (attr_info->worst <= threshold_info->threshold);
    }

    if (failed_now) {
        attr_status = "FAILING_NOW";
    } else if (failed_ever) {
        attr_status = "IN_THE_PAST";
    } else {
        attr_status = "    -";
    }
    Json *str_failed_obj = NULL;
    JsonStringCreate(attr_status, &str_failed_obj);
    JsonObjectItemSet(smart_obj, SMART_WHEN_FAILED, str_failed_obj);

    *output_jso = smart_obj;
    return RET_OK;
}

/*
 * Description: 获取硬盘的SMART信息，并以字符串形式输出
 * History: 1.2018年5月19日
 * 新生成函数
 */
gint32 sml_get_smart_data_str(guint8 controller_index, guint16 pd_device_id, guint8 hdd_intf_type,
    GVariant **smart_info_var)
{
    gint32 ret;
    guint16 idx;
    guint16 idx1 = 0;
    SML_PD_SMART_COLLECT_INFO_S pd_smart_info = { 0 };
    ATA_SMART_DATA_S *smart_data = NULL;
    ATA_SMART_THRESHOLDS_S *smart_threshold = NULL;
    guchar              output_data[MAX_PROPERTY_VALUE_LEN] = {0}; /* 最多30个信息项，此时id+val共占60个字节，缓冲区足够 */
    Json *item_obj = NULL;

    pd_smart_info.i_controller_index = controller_index;
    pd_smart_info.i_device_id = pd_device_id;
    ret = sml_collect_hdd_smart_data(&pd_smart_info);
    if (ret != RET_OK) {
        debug_log(DLOG_ERROR, "%s: cann't get hdd smart info with raid id [%d], pd device id [%d]", __FUNCTION__,
            controller_index, pd_device_id);
        return ret;
    }

    if (pd_smart_info.smart_info.valid_flag == FALSE) {
        debug_log(DLOG_INFO, "%s: smart info for drive[%u] is not valid", __FUNCTION__, pd_device_id);
        return RET_ERR;
    }

    if (hdd_intf_type == PD_INTERFACE_TYPE_SATA) {
        smart_data = (ATA_SMART_DATA_S *)pd_smart_info.smart_info.SATADevice.smart_data;
        smart_threshold = (ATA_SMART_THRESHOLDS_S *)pd_smart_info.smart_info.SATADevice.smart_threshold;

        (void)JsonArrayCreate(&item_obj);
        for (idx = 0; idx < sizeof(smart_data->smartAttribute) / sizeof(smart_data->smartAttribute[0]);
            idx++) { /* smart 信息的个数为30 */
            ATA_SMART_ATTRIBUTE_S *attr_info = &smart_data->smartAttribute[idx];
            ATA_SMART_THRESHOLD_ENTRY_S *threshold_info = &smart_threshold->thresholdEntries[idx];

            if (attr_info->id) {
                // 新增获取SMART全量信息
                Json *smart_obj = NULL;
                ret = get_all_smart_data_to_json(attr_info, threshold_info, &smart_obj);
                if (ret != RET_OK) {
                    debug_log(DLOG_ERROR, "%s: get all smart info failed", __FUNCTION__);
                    (void)JsonObjectRelease(smart_obj);
                    continue;
                }
                // 每条smart信息以json数组的格式保存
                guint32 array_size = 0;
                JsonArraySizeGet(item_obj, &array_size);
                JsonArrayItemInsert(item_obj, array_size, smart_obj);
            }
        }
        *smart_info_var = g_variant_new_string(JsonPrintWithoutFormat(item_obj));
        (void)JsonObjectRelease(item_obj);
    } else if (hdd_intf_type == PD_INTERFACE_TYPE_SAS) {
        output_data[idx1++] = SAS_SMART_ASC_COLLECT_ID;
        output_data[idx1++] = pd_smart_info.smart_info.SASDevice.ASC;

        output_data[idx1++] = SAS_SMART_ASCQ_COLLECT_ID;
        output_data[idx1++] = pd_smart_info.smart_info.SASDevice.ASCQ;

        *smart_info_var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, output_data, idx1, sizeof(guchar));
    }

    if (NULL == *smart_info_var) {
        debug_log(DLOG_ERROR, "%s:new result byte fixed array failed", __FUNCTION__);
        return RET_ERR;
    }
    return RET_OK;
}

/*
 * Description: APP通过SML对PD执行操作
 * History: 1.2016年5月7日
 *          新生成函数
 */
gint32 sml_pd_operation(SML_PD_OPERTATION_S *pd)
{
    gint32 ret_val = 0;
    guint8 ctrl_index = 0;
    guint16 device_id = 0;
    guint8 operation = 0;

    if (pd == NULL) {
        return SML_ERR_NULL_DATA;
    }

    ctrl_index = pd->i_controller_index;
    device_id = pd->i_device_id;
    operation = pd->i_operation;

    ret_val = smlib_exec_pd_operation(ctrl_index, device_id, operation, (gpointer)pd->i_param_ptr, pd->i_param_size);
    if (SML_SUCCESS != ret_val) {
        debug_log(DLOG_ERROR, "sml_pd_operation failed, ctrl index = %d, pd id = %d, return 0x%0x\n", ctrl_index,
            device_id, ret_val);
    }

    return ret_val;
}

/*
 * Description: 打印ATA smart 信息到buf
 * History: 1.2017年5月9日
 */
void sml_parse_ata_smart_attr_raw_value(gchar *buf, guint32 buf_size, ATA_SMART_ATTRIBUTE_S *smart_attr, guint8 *format)
{
    smlib_parse_ata_smart_attr_raw_value(buf, buf_size, smart_attr, format);
}

/*
 * Description: 获取SMART Attribute的名称
 * History: 1.2018年1月10日
 *          新生成函数
 */
const gchar *sml_get_ata_smart_attr_name(guint8 attr_id)
{
    return smlib_get_ata_smart_attr_name(attr_id);
}

/*
 * Description: APP通过SML检查pd的sense code，判断是否需要上报故障
 * History: 1.2019年2月15日,
 *          新生成函数
 *          2.2019年10月24日,
 *          UADP432406 剥离LSI的SDK代码
 */
gboolean sml_check_pd_sense_code(SCSI_SENSE_DISECT_S *sense_info)
{
    guint32 i = 0;
    guint8 key_find = 1;
    guint8 asc_find = 1;
    guint8 ascq_find = 1;
    guint8 retval = FALSE;
    SCSI_SENSE_DISECT_S error_sense[] = {
        { 0xff, 0xff, SCSI_ASC_FAILURE_THRESHOLD_EXCEEDED, 0xff },
        // medium error
        { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_UNRECOVERED_READ,     SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
        { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_UNRECOVERED_READ,     SCSI_ASCQ_FORMAT_IN_PROGRESS },
        { 0xff, SCSI_KEY_MEDIUM_ERROR, SCSI_ASC_DATA_SYNC_MARK_ERROR, SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
        // Hardware error
        { 0xff, SCSI_KEY_HARDWARE_ERROR, SCSI_ASC_DATA_SYNC_MARK_ERROR, SCSI_ASCQ_CAUSE_NOT_REPORTABLE },
        { 0xff, SCSI_KEY_HARDWARE_ERROR, SCSI_ASC_LOGICAL_UNIT_FAILURE, SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED },
        // other error
        { 0xff, SCSI_KEY_MISCOMPARE, SCSI_ASC_DURING_VERIFY_BYTE_CHECK_OPEARTION, SCSI_ASCQ_CAUSE_NOT_REPORTABLE }
    };
    guint32 num = (guint32)(sizeof(error_sense) / sizeof(error_sense[0]));

    for (i = 0; i < num; i++) {
        key_find = TRUE;
        asc_find = TRUE;
        ascq_find = TRUE;
        // 0xff表示任意数值都匹配直接标记match，无需比较，否则进行比较标记
        if (error_sense[i].sense_key != 0xff) {
            key_find = sense_info->sense_key == error_sense[i].sense_key ? TRUE : FALSE;
        }
        if (error_sense[i].asc != 0xff) {
            asc_find = sense_info->asc == error_sense[i].asc ? TRUE : FALSE;
        }
        if (error_sense[i].ascq != 0xff) {
            ascq_find = sense_info->ascq == error_sense[i].ascq ? TRUE : FALSE;
        }
        // 如果三项都匹配，说明硬盘故障，直接返回异常
        if (key_find && asc_find && ascq_find) {
            retval = TRUE;
            break;
        }
    }

    return (gboolean)retval;
}
